package ca.polymtl.rubikcube.CubeActivity.model;

import ca.polymtl.rubikcube.util.Color;
import ca.polymtl.rubikcube.util.Position;

public class Rubik {
	public enum RotationDirection {
		CCW, CW
	};

	/*
	 * I know we all know that the cube dimension is 3, but it will probably be much clearer in the code with that.
	 */
	public final static int CUBEDIM = 3;

	/*
	 * The 27 cubelets of the cube
	 */
	private Cubelet[][][] cubelets = new Cubelet[CUBEDIM][CUBEDIM][CUBEDIM];
	
	public Rubik() {
		initCube();
	}

	public Cubelet cubeletAt(int x, int y, int z) {
		return cubelets[x][y][z];
	}

	public Cubelet cubeletAt(Position pos) {
		return cubeletAt(pos.getX(), pos.getY(), pos.getZ());
	}

	/**
	 * Puts the cube in its initial state.
	 */
	private void initCube() {
		/* Create the cubelets and initialize them to black */
		for (int i = 0; i < CUBEDIM; i++) {
			for (int j = 0; j < CUBEDIM; j++) {
				for (int k = 0; k < CUBEDIM; k++) {
					cubelets[i][j][k] = new Cubelet();
					cubelets[i][j][k].setAllSidesColor(Color.BLACK);
					cubelets[i][j][k].pos = new Position(i, j, k);
				}
			}
		}

		/* Create the faces and colour the cube */
		for (int i = 0; i < 6; i++) {
			Face thisFace = new Face(this, Side.values()[i]);
			thisFace.assignAll(Color.getColorById(i));
		}
	}

	public void rotateFace(Side side, RotationDirection way) {
		CubeTransitions tr = CubeTransitions.getTransitions(side, way);
		Position[] corners = tr.corners;
		Position[] sides = tr.sides;

		Cubelet tmp;

		/* Swap the four cubelets at the corners */
		tmp = cubelets[corners[0].getX()][corners[0].getY()][corners[0].getZ()];
		cubelets[corners[0].getX()][corners[0].getY()][corners[0].getZ()] = cubelets[corners[1].getX()][corners[1]
				.getY()][corners[1].getZ()];
		cubelets[corners[1].getX()][corners[1].getY()][corners[1].getZ()] = cubelets[corners[2].getX()][corners[2]
				.getY()][corners[2].getZ()];
		cubelets[corners[2].getX()][corners[2].getY()][corners[2].getZ()] = cubelets[corners[3].getX()][corners[3]
				.getY()][corners[3].getZ()];
		cubelets[corners[3].getX()][corners[3].getY()][corners[3].getZ()] = tmp;

		/* Swap the four cubelets on the sides */
		tmp = cubelets[sides[0].getX()][sides[0].getY()][sides[0].getZ()];
		cubelets[sides[0].getX()][sides[0].getY()][sides[0].getZ()] = cubelets[sides[1].getX()][sides[1].getY()][sides[1]
				.getZ()];
		cubelets[sides[1].getX()][sides[1].getY()][sides[1].getZ()] = cubelets[sides[2].getX()][sides[2].getY()][sides[2]
				.getZ()];
		cubelets[sides[2].getX()][sides[2].getY()][sides[2].getZ()] = cubelets[sides[3].getX()][sides[3].getY()][sides[3]
				.getZ()];
		cubelets[sides[3].getX()][sides[3].getY()][sides[3].getZ()] = tmp;

		/* Ask the cubelets on the corners to swap their colors */
		for (Position p : corners) {
			cubeletAt(p).rotate(side, way);
		}

		/* Ask the cubelets on the sides to swap their colors */
		for (Position p : sides) {
			cubeletAt(p).rotate(side, way);
		}
	}

	public Cubelet[][][] getCubelets() {
		return cubelets;
	}
	
	public Face getFace(Side side) {
		return new Face(this, side);
	}
	
	public void assignColors(int[] initialColors) {	
		for (int face = 0; face < 6; face++) {
			Face thisFace = new Face(this, Side.values()[face]);
			
			for (int j = 0; j < 3; j++) {
				for (int i = 0; i < 3; i++) {
					int colorId = initialColors[face * 9 + j * 3 + i];
					Color color = Color.getColorById(colorId);
					thisFace.assign(i, j, color);
				}
					
			}
		}
	}
}
